package com.azhoucode.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.azhoucode.constant.RedisConstant;
import com.azhoucode.constant.TipTemplateConstant;
import com.azhoucode.entity.ApplicationEntity;
import com.azhoucode.entity.LoginParam;
import com.azhoucode.entity.RouterParam;
import com.azhoucode.entity.TableParam;
import com.azhoucode.enums.TemplateTypeEnum;
import com.azhoucode.service.GenerateService;
import com.azhoucode.util.FreeMarkerUtil;
import com.azhoucode.util.JsonUtils;
import com.azhoucode.util.RedisCacheUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;


@Service
public class GenerateServiceImpl implements GenerateService {

    @Autowired
    private FreeMarkerUtil freeMarkerUtil;


    @Value("${file-path.project}")
    private String pomFilePath;

    @Value("${application}")
    private String appFilePath;

    @Value("${config}")
    private String configFilePath;

    @Value("${util}")
    private String utilFilePath;

    @Value("${controller}")
    private String controllerFilePath;

    @Value("${dao}")
    private String daoFilePath;

    @Value("${service}")
    private String serviceFilePath;

    @Value("${serviceImpl}")
    private String serviceImplFilePath;

    @Value("${entity}")
    private String entityFilePath;

    @Value("${dto}")
    private String dtoFilePath;

    @Value("${xml}")
    private String xmlFilePath;

    @Value("${vueTable}")
    private String vueElementTablePath;

    @Value("${vueTableApi}")
    private String vueElementTableApiPath;

    @Value("${vueRouter}")
    private String vueElementRouterPath;

    @Value("${vueCommonTable}")
    private String vueCommonTablePath;
    @Value("${vueCommonRouterPath}")
    private String vueCommonRouterPath;
    @Value("${vueCommonSidebarPath}")
    private String vueCommonSidebarPath;

    @Value("${vueTable2}")
    private String vueTable2;
    @Value("${vueTableApi2}")
    private String vueTableApi2;
    @Value("${vueRouter3}")
    private String vueRouter3;
    @Override
    public void generateCode(String random, Integer type) throws Exception {
        this.generateProjectCommonCode(random);
        this.generateBusinessCode(random, type);
    }

    @Override
    public void generateClassCode(Map<String, Object> tempData, Integer type, String className, String saveUrl) throws Exception {
        TemplateTypeEnum templateTypeEnum = TemplateTypeEnum.getByCode(type);
        Assert.notNull(templateTypeEnum, TipTemplateConstant.TEMP_TYPE_NOT_FOUND, type);
        freeMarkerUtil.generate(tempData, templateTypeEnum.getFtlName(), saveUrl, String.format(templateTypeEnum.getFileName(), className));
    }

    @Override
    public void generatePublicCode(Map<String, Object> tempData, Integer type, String saveUrl) throws Exception {
        TemplateTypeEnum templateTypeEnum = TemplateTypeEnum.getByCode(type);
        Assert.notNull(templateTypeEnum, TipTemplateConstant.TEMP_TYPE_NOT_FOUND, type);
        freeMarkerUtil.generate(tempData, templateTypeEnum.getFtlName(), saveUrl, templateTypeEnum.getFileName());
    }

    /**
     * 生成项目公共代码
     *
     * @throws Exception
     */
    public void generateProjectCommonCode(String random) throws Exception {
        ApplicationEntity applicationEntity = RedisCacheUtils.get(String.format(RedisConstant.DB_PARAM, random), ApplicationEntity.class);
        // 生成application代码
        Map<String, Object> stringObjectMap = JsonUtils.jsonToMap(JsonUtils.objectToJson(applicationEntity));
        this.generatePublicCode(stringObjectMap, TemplateTypeEnum.APPLICATION.getType(), appFilePath);
        this.generatePublicCode(stringObjectMap, TemplateTypeEnum.POM.getType(), pomFilePath);
        if (applicationEntity.getHasEmail())
            this.generatePublicCode(stringObjectMap, TemplateTypeEnum.MAIN_UTIL.getType(), utilFilePath);
        if (applicationEntity.getHasRedis()) {
            this.generatePublicCode(stringObjectMap, TemplateTypeEnum.REDIS_UTIL.getType(), utilFilePath);
            this.generatePublicCode(stringObjectMap, TemplateTypeEnum.REDIS_CONFIG.getType(), configFilePath);
        }
        // 需要自定义登录
        if (applicationEntity.getHasLogin() && applicationEntity.getLoginParam() != null) {
            LoginParam loginParam = applicationEntity.getLoginParam();
            if (StrUtil.isNotEmpty(loginParam.getTableName()) &&
                    StrUtil.isNotEmpty(loginParam.getUserNameCol()) &&
                    StrUtil.isNotEmpty(loginParam.getPassWordCol())) {
                loginParam.setClassName(freeMarkerUtil.getEntityName(loginParam.getTableName()));
                loginParam.setUnClassName(freeMarkerUtil.getEntityNameLower(loginParam.getTableName()));
                Map<String, Object> loginParamMap = JsonUtils.jsonToMap(JsonUtils.objectToJson(applicationEntity.getLoginParam()));
                this.generatePublicCode(loginParamMap, TemplateTypeEnum.LOGIN_CONTROLLER.getType(), controllerFilePath);
            }
        }
    }


    /**
     * 生成业务代码
     *
     * @param type 前端模板类型【1：vue-element 2：普通vue 3：html+ layui】
     * @throws Exception
     */
    public void generateBusinessCode(String random, @Nullable Integer type) throws Exception {
        List<String> tableDataList = RedisCacheUtils.lrange(String.format(RedisConstant.TABLE_LIST, random), 0, -1);
        if (CollectionUtil.isEmpty(tableDataList)) return;
        List<TableParam> tableParamList = tableDataList.stream().map(item -> JSON.parseObject(item, TableParam.class)).collect(Collectors.toList());
        // 倒叙生成 表重复时覆盖之前的设置
        List<TableParam> reverse = CollectionUtil.reverse(tableParamList);
        for (TableParam tableParam : reverse) {
            Map<String, Object> stringObjectMap = JsonUtils.jsonToMap(JsonUtils.objectToJson(tableParam));
            // 前端代码生成每个文件对应的文件夹名称，根据选择不同的模板来生成不同的前端
//            if (type == 1) generateVueElementCode(stringObjectMap, tableParam);
//            else if (type == 2) generateVueCode(stringObjectMap, tableParam);
//            else if (type == 3) generateVueElementCode2(stringObjectMap, tableParam);
            // 后端代码生成，不变
            this.generateClassCode(stringObjectMap, TemplateTypeEnum.ENTITY.getType(), tableParam.getClassName(), entityFilePath);
            this.generateClassCode(stringObjectMap, TemplateTypeEnum.DAO.getType(), tableParam.getClassName(), daoFilePath);
            this.generateClassCode(stringObjectMap, TemplateTypeEnum.SERVICE.getType(), tableParam.getClassName(), serviceFilePath);
            this.generateClassCode(stringObjectMap, TemplateTypeEnum.SERVICE_IMPL.getType(), tableParam.getClassName(), serviceImplFilePath);
            this.generateClassCode(stringObjectMap, TemplateTypeEnum.DTO.getType(), tableParam.getClassName(), dtoFilePath);
            this.generateClassCode(stringObjectMap, TemplateTypeEnum.CONTROLLER.getType(), tableParam.getClassName(), controllerFilePath);
            this.generateClassCode(stringObjectMap, TemplateTypeEnum.XML.getType(), tableParam.getClassName(), xmlFilePath);
        }
//        Map<String, Object> map = new HashMap<>();
//        List<RouterParam> routerParams = new ArrayList<>();
//        reverse.stream().collect(Collectors.toMap(item -> item.getTableName(), item -> item, (o1, o2) -> o2)).entrySet().forEach(item -> {
//            RouterParam routerParam = new RouterParam();
//            routerParam.setRouterName(item.getValue().getUnClassName());
//            routerParam.setChinaName(item.getValue().getTableComment());
//            routerParams.add(routerParam);
//        });
//        map.put("routerParams", routerParams);
//        if (type == 1)  this.generatePublicCode(map, TemplateTypeEnum.VUE_ROUTER.getType(), vueElementRouterPath);
//        else if(type == 2) {
//            this.generatePublicCode(map, TemplateTypeEnum.VUE_ROUTER2.getType(), vueCommonRouterPath);
//            this.generatePublicCode(map, TemplateTypeEnum.VUE_SIDEBAR.getType(), vueCommonSidebarPath);
//        }else if(type == 3){
//            this.generatePublicCode(map, TemplateTypeEnum.VUE_ROUTER3.getType(), vueRouter3);
//        }
    }

    public void generateVueElementCode(Map<String, Object> stringObjectMap, TableParam tableParam) throws Exception {
        String vueTable = String.format(vueElementTablePath, tableParam.getUnClassName());
        String vueApi = String.format(vueElementTableApiPath, tableParam.getUnClassName());
        this.generatePublicCode(stringObjectMap, TemplateTypeEnum.VUE_TABLE.getType(), vueTable);
        this.generatePublicCode(stringObjectMap, TemplateTypeEnum.VUE_API.getType(), vueApi);
    }

    public void generateVueCode(Map<String, Object> stringObjectMap, TableParam tableParam) throws Exception {
        String vueTable = String.format(vueCommonTablePath, tableParam.getUnClassName());
        this.generateClassCode(stringObjectMap, TemplateTypeEnum.VUE_TABLE2.getType(),  tableParam.getUnClassName(), vueTable);
    }

    public void generateVueElementCode2(Map<String, Object> stringObjectMap, TableParam tableParam) throws Exception {
        String vueTable = String.format(vueTable2, tableParam.getUnClassName());
        String vueApi = String.format(vueTableApi2, tableParam.getUnClassName());
        this.generatePublicCode(stringObjectMap, TemplateTypeEnum.VUE_TABLE.getType(), vueTable);
        this.generatePublicCode(stringObjectMap, TemplateTypeEnum.VUE_API.getType(), vueApi);
    }
}
